package infos

import (
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/app/ginsrv/api/base"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/columnd"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/common"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/infod"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/util"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/ddd"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/internal/json"
)

func createInfoCtrl(d core.CmsDomain) *infoCtrl {
	return &infoCtrl{
		BaseContrl: base.BaseContrl{
			Domain: d,
		},
		info: d.Info(),
		iar:  d.Info().InfoAggregateRoot(),
		car:  d.Column().ColumnAggregateRoot(),
	}
}

type infoCtrl struct {
	base.BaseContrl
	info infod.InfoDomain
	iar  infod.InfoAggregateRoot
	car  columnd.ColumnAggregateRoot
}

func (c infoCtrl) geInfoSearch(ctx *gin.Context) (result infod.InfoSearch, err error) {

	result.ColumnID = ctx.DefaultQuery("column", "")
	result.Key = ctx.DefaultQuery("key", "")
	result.OrgID = c.GetOrgID(ctx)
	result.Range = c.GetIntByQuery(ctx, "range", 0)
	result.Tags = ctx.QueryArray("tags")
	result.UID = ctx.DefaultQuery("uid", "")
	ss := c.GetIntArrayByQuery(ctx, "status", nil)
	if len(ss) > 0 {
		for _, v := range ss {
			result.Status = append(result.Status, infod.IStatus(v))
		}
	}
	if extrastr, ok := ctx.GetQuery("extral"); ok && extrastr != "" {
		if err = json.Unmarshal([]byte(extrastr), &result.Extral); err != nil {
			return
		}
	}
	// 获取发布时间查询
	result.PubTime = c.GetSchTimeByQuery(ctx, "pubTime")
	// 获取创建时间查询
	result.CreTime = c.GetSchTimeByQuery(ctx, "creTime")

	return
}

// 分页获取信息列表
func (c infoCtrl) getInfoPageList(ctx *gin.Context) {

	page := c.GetPageInfo(ctx, common.PageModeNum)
	dctx := c.DDDContext(ctx)
	sch, err := c.geInfoSearch(ctx)
	if err != nil {
		c.ErrBadRequestByBind(ctx, 1, err)
		return
	}
	pr, cs, err := c.iar.QueryPage(dctx, sch, page)
	if err != nil {
		c.ErrServerError(ctx, 1, "查找信息列表失败", errors.Wrap(err, "查找信息列表失败"))
		return
	}

	ncs := make([]infod.InfoAggregate, 0, len(cs))
	for _, v := range cs {
		if v.Title == "" && v.Kind == infod.KindSimplify {
			v.Title = v.Content
		}
		v.Content = ""
		ncs = append(ncs, v)
	}
	c.SuccessPage(ctx, page, pr, ncs)
}

// ReqInfoDesc 请求参数
type ReqInfoDesc struct {
	ColumnID string                 `json:"column_id,omitempty" binding:"required,max=30"` // 所属栏目
	Kind     int                    `json:"kind,omitempty" binding:"required,gte=1,lte=5"` // 类型 1.文章，2短文，3投票，4活动，5调查问卷
	Title    string                 `json:"title,omitempty" binding:"required,max=100"`    // 标题
	SubTitle string                 `json:"sub_title,omitempty" binding:"max=100"`         // 副标题标题
	ListMode int                    `json:"list_mode,omitempty"`                           // 列表展示时的默认模式
	Icon     []string               `json:"icon,omitempty"`                                // 图标
	Banner   string                 `json:"banner,omitempty"`                              // 封面
	From     string                 `json:"from,omitempty"`                                // 来源
	Desc     string                 `json:"desc,omitempty" binding:"max=500"`              // 简介
	Tags     []string               `json:"tags,omitempty"`                                // 标签列表
	Content  string                 `json:"content,omitempty"  binding:"max=5000"`         // 信息内容
	Extras   map[string]interface{} `json:"extras,omitempty"`                              // 扩展信息
	Org      string                 `json:"_"`                                             // 所属组织
}

// To 转换为infod.InfoDesc
func (r ReqInfoDesc) To() infod.InfoDesc {
	return infod.InfoDesc{
		ColumnID: r.ColumnID,
		Kind:     r.Kind,
		Title:    r.Title,
		SubTitle: r.SubTitle,
		ListMode: r.ListMode,
		Icon:     r.Icon,
		From:     r.From,
		Banner:   r.Banner,
		Desc:     r.Desc,
		Tags:     r.Tags,
		Content:  r.Content,
		Extras:   r.Extras,
		Org:      r.Org,
	}
}

func (c infoCtrl) add(ctx *gin.Context) {
	var req ReqInfoDesc

	if err := ctx.BindJSON(&req); err != nil {
		c.ErrBadRequestByBind(ctx, 1, err)
		return
	}
	dctx := c.DDDContext(ctx)
	desc := req.To()
	desc.Org = c.GetOrgID(ctx)
	col, err := c.car.GetColumn(dctx, desc.ColumnID)
	if err != nil {
		c.ErrServerError(ctx, 2, "保存失败", errors.Wrap(err, "检测栏目是否存在失败."))
		return
	}
	if col == nil {
		c.ErrBadRequest(ctx, 3, "栏目不存在")
		return
	}
	if col.Status < 0 {
		c.ErrBadRequest(ctx, 4, "栏目状态不对，不能添加信息")
		return
	}
	if col.ProhibitPostInfo == columnd.On {
		c.ErrBadRequest(ctx, 5, "栏目模式,禁止发文章")
		return
	}
	if col.Kind == columnd.ColumnKindCommon {
		desc.Org = col.Org
		desc.Owner = col.Owner
	}
	if err := util.CheckInfoExtral(col.Extras, req.Extras); err != nil {
		c.ErrServerError(ctx, 6, fmt.Sprintf("保存失败:%v", err.Error()), errors.Wrapf(err, "检查扩展属性失败"))
		return
	}
	if col.InfoMode == columnd.InfoModeSingle {
		// 单文章模式发文章
		id, err := c.addSingleInfo(dctx, col, &desc)
		if err != nil {
			c.ErrServerError(ctx, 7, "保存失败", errors.Wrapf(err, "单文章模式发文失败"))
			return
		}
		c.Success(ctx, map[string]interface{}{"info_id": id})
		return
	}

	id, err := c.iar.AddInfo(dctx, desc)
	if err != nil {
		c.ErrServerError(ctx, 6, "保存失败", errors.Wrap(err, "新增信息失败："))
		return
	}
	c.Success(ctx, map[string]interface{}{"info_id": id})
}

func (c infoCtrl) addSingleInfo(dctx ddd.Context, col *columnd.ColumnAggregate, desc *infod.InfoDesc) (id string, err error) {
	pr, list, err := c.iar.QueryPage(dctx,
		infod.InfoSearch{
			ColumnID: col.ColumnID,
			Status: []infod.IStatus{
				infod.IStatusInit,
				infod.IStatusCheckWait,
				infod.IStatusCheckNO,
				infod.IStatusCheckOk,
				infod.IStatusNoPublish,
			},
		}, common.PageInfo{
			Mode:     0,
			PageSize: 1,
			Desc:     true,
		})
	if err != nil {
		return "", err
	}
	if pr.Total > 0 {
		// 已经有文章,如果有在编辑的文章不能新增
		for _, i := range list {
			// 已存在文章没有发布不能新增,只能修改
			return i.InfoID, i.UpdateDesc(dctx, desc)
		}
	}
	return c.iar.AddInfo(dctx, *desc)
}

func (c infoCtrl) updateDesc(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误")
		return
	}
	var req ReqInfoDesc

	if err := ctx.BindJSON(&req); err != nil {
		c.ErrBadRequestByBind(ctx, 2, err)
		return
	}

	dctx := c.DDDContext(ctx)

	i, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 3, "保存失败", errors.Wrap(err, "检测信息是否存在失败."))
		return
	}
	if i == nil {
		c.ErrNotFound(ctx, 4, fmt.Sprintf("信息(%v)不存在", infoid))
		return
	}
	if i.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	desc := req.To()

	col, err := c.car.GetColumn(dctx, desc.ColumnID)
	if err != nil {
		c.ErrServerError(ctx, 5, "保存失败", errors.Wrap(err, "检测栏目是否存在失败."))
		return
	}
	if col == nil {
		c.ErrBadRequest(ctx, 6, "栏目不存在")
		return
	}
	if col.Status == -1 {
		c.ErrBadRequest(ctx, 7, "栏目状态不对，不能添加信息")
		return
	}
	if err := util.CheckInfoExtral(col.Extras, req.Extras); err != nil {
		c.ErrServerError(ctx, 8, fmt.Sprintf("保存失败:%v", err.Error()), errors.Wrapf(err, "单文章模式发文失败"))
		return
	}
	if col.InfoMode == columnd.InfoModeSingle {
		// 单文章模式发文章
		_, err := c.addSingleInfo(dctx, col, &desc)
		if err != nil {
			c.ErrServerError(ctx, 9, "保存失败", errors.Wrapf(err, "单文章模式发文失败"))
			return
		}
		c.Success(ctx, nil)
		return
	}
	if col.Kind == columnd.ColumnKindCommon {
		desc.Org = col.Org
		desc.Owner = col.Owner
	}
	err = i.UpdateDesc(dctx, &desc)
	if err != nil {
		c.ErrServerError(ctx, 10, "保存失败", errors.Wrapf(err, "修改信息(%v)失败.", infoid))
		return
	}
	c.Success(ctx, nil)
}

func (c infoCtrl) delete(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	dctx := c.DDDContext(ctx)
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "删除失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.ErrNotFound(ctx, 3, "信息不存在", nil)
		return
	}
	if ia.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	err = ia.Delete(dctx)
	if err != nil {
		c.ErrServerError(ctx, 4, "删除失败", errors.Wrapf(err, "删除信息(%v)失败：", infoid))
		return
	}
	c.Success(ctx, nil)
}
func (c infoCtrl) destroy(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	dctx := c.DDDContext(ctx)
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "彻底删除失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.Success(ctx, nil)
		return
	}
	if ia.Status != infod.IStatusDeleted {
		c.ErrBadRequest(ctx, 3, "未删除不能彻底删除", errors.Wrapf(err, "检测信息(%v)状态不是删除状态：", infoid))
		return
	}
	if ia.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	err = ia.Destroy(dctx)
	if err != nil {
		c.ErrServerError(ctx, 4, "彻底删除失败", errors.Wrapf(err, "彻底删除信息(%v)失败：", infoid))
		return
	}
	c.Success(ctx, nil)
}
func (c infoCtrl) recover(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	dctx := c.DDDContext(ctx)
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "恢复失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.Success(ctx, nil)
		return
	}
	if ia.Status != infod.IStatusDeleted {
		c.ErrBadRequest(ctx, 3, "未删除不能恢复", errors.Wrapf(err, "检测信息(%v)状态不是删除状态：", infoid))
		return
	}
	if ia.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	err = ia.Recover(dctx)
	if err != nil {
		c.ErrServerError(ctx, 4, "恢复失败", errors.Wrapf(err, "恢复信息(%v)失败：", infoid))
		return
	}
	c.Success(ctx, nil)
}

// 获取指定信息
func (c infoCtrl) getInfo(ctx *gin.Context) {

	infoid := ctx.Param("infoid")
	dctx := c.DDDContext(ctx)
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "查找信息失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.ErrNotFound(ctx, 3, "信息不存在", nil)
		return
	}

	c.Success(ctx, ia)
}

func (c infoCtrl) publish(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	dctx := c.DDDContext(ctx)
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "删除失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.ErrNotFound(ctx, 3, "信息不存在", nil)
		return
	}
	if ia.Status == infod.IStatusDeleted {
		c.ErrBadRequest(ctx, 11, "信息已经删除，不能发布")
		return
	}
	if ia.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	if ca, err := c.car.GetColumn(dctx, ia.ColumnID); err != nil {
		c.ErrServerError(ctx, 2, "查找信息所在栏目失败", errors.Wrapf(err, "查找(%v)信息所在栏目：", infoid))
		return
	} else if ca != nil {
		if ca.Status == -1 {
			c.ErrServerError(ctx, 2, "栏目已经删除不能在发布信息")
			return
		}
		if ca.IsInfoChk == 1 && ia.Status != infod.IStatusCheckOk {
			// 需要审核
			c.ErrBadRequest(ctx, 10, "信息没有审核通过，不能发布")
			return
		} else if ca.IsInfoChk == 0 && ia.Status != infod.IStatusInit && ia.Status != infod.IStatusNoPublish {
			// 需要状态为初始化
			c.ErrBadRequest(ctx, 10, "信息状态不正确不能发布")
			return
		}
	}
	err = ia.Publish(dctx)
	if err != nil {
		c.ErrServerError(ctx, 4, "发布失败", errors.Wrapf(err, "发布信息(%v)失败：", infoid))
		return
	}
	c.Success(ctx, nil)
}

func (c infoCtrl) unpublish(ctx *gin.Context) {
	infoid := ctx.Param("infoid")
	if infoid == "" {
		c.ErrBadRequest(ctx, 1, "请求参数错误", nil)
		return
	}
	dctx := c.DDDContext(ctx)
	ia, err := c.iar.GetInfo(dctx, infoid)
	if err != nil {
		c.ErrServerError(ctx, 2, "删除失败", errors.Wrapf(err, "检测信息(%v)是否存在失败：", infoid))
		return
	}
	if ia == nil {
		c.ErrNotFound(ctx, 3, "信息不存在", nil)
		return
	}
	if ia.Status != infod.IStatusPublished {
		c.ErrBadRequest(ctx, 11, "信息不是发布状态，不能取消发布")
		return
	}
	if ia.Org != c.GetOrgID(ctx) {
		c.ErrForbidden(ctx, "无权限访问该文章")
		return
	}
	err = ia.Unpublish(dctx)
	if err != nil {
		c.ErrServerError(ctx, 4, "取消发布失败", errors.Wrapf(err, "取消发布信息(%v)失败：", infoid))
		return
	}
	c.Success(ctx, nil)
}
